package com.wys.spring.sharding;

import com.wys.spring.ApplicationContextHolder;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.shardingsphere.core.rule.TableRule;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.connection.ShardingConnection;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.datasource.ShardingDataSource;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.statement.ShardingStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

public class ShardingRuleSpringRunner implements CommandLineRunner {


    private static final Logger log = LoggerFactory.getLogger(ShardingRuleSpringRunner.class);

    /**
     * 获取建表语句
     *
     * @param table  表名
     * @param DbName 数据库名
     * @return
     */
    public static String getDbSql(Connection conn, String table, String newTableName, String DbName) {
        try {
            DatabaseMetaData metaData = conn.getMetaData();
            Set<String> primaryKeySet = new LinkedHashSet<>();
            Map<String, String> columnMap = new LinkedHashMap<>();
            ResultSet primaryKeys = metaData.getPrimaryKeys(null, null, table);
            while (primaryKeys.next()) {
                String columnName = primaryKeys.getString("COLUMN_NAME");
                primaryKeySet.add(columnName);
            }
            ResultSet resultSet = metaData.getColumns(DbName, null, table, "%");
            while (resultSet.next()) {
                String columnName = resultSet.getString("COLUMN_NAME");
                String dataType = resultSet.getString("TYPE_NAME");
                int columnSize = resultSet.getInt("COLUMN_SIZE");
                String notNull = resultSet.getString("IS_NULLABLE");
                Optional<String> key = columnMap.keySet().stream().filter(primaryKeySet::contains).findFirst();
                if (key.isPresent()) {
                    columnMap.put(key.get(), columnMap.get(key.get()) + " PRIMARY KEY ");
                } else {
                    columnMap.put(columnName, "`" + columnName + "`" + "  " + dataType + (dataType.equalsIgnoreCase("varchar") ? " (" + columnSize + ") " : "") + (notNull.equalsIgnoreCase("YES") ? "" : " not null "));
                }
            }
            //fixme 数据据连接用完一定要关掉，不然会有很多奇怪的bug
            conn.close();
            if (!columnMap.isEmpty()) {
                String createTableSQL = "CREATE TABLE " + newTableName + " (\n";
                createTableSQL += String.join(",\n", columnMap.values());
                createTableSQL += "\n);";
                // 构造创建语句成功，继续下一步操作
                return createTableSQL;
            } else {
                log.warn("No columns found for the table!");
            }
        } catch (Exception e) {
            log.warn("获取建表语句异常:", e);
        }
        return null;
    }

    @Override
    public void run(String... args) throws Exception {
        ShardingDataSource shardingDataSource = ApplicationContextHolder.context.getBean(ShardingDataSource.class);
        Collection<TableRule> tableRules = shardingDataSource.getRuntimeContext().getRule().getTableRules();
        if (ObjectUtils.isNotEmpty(tableRules)) {
            for (TableRule tableRule : tableRules) {
                tableRule.getDatasourceToTablesMap().keySet().forEach(k -> {
                    tableRule.getDatasourceToTablesMap().get(k).forEach(s -> {
                        ShardingConnection connection = shardingDataSource.getConnection();
                        if (!shardingDataSource.getRuntimeContext().getMetaData().getSchema().containsTable(s)) {
                            String createSql = getDbSql(connection, tableRule.getLogicTable(), s, shardingDataSource.getRuntimeContext().getMetaData().getDataSources().getDataSourceMetaData(k).getCatalog());
                            log.info("-----------|监测到shardingJdbc还有数据表未创建开始执行建表语句|:{}", createSql);
                            ShardingStatement statement = (ShardingStatement) connection.createStatement();
                            try {
                                statement.execute(createSql);
                                connection.close();
                            } catch (SQLException e) {
                                log.error("sql异常:", e);
                                throw new RuntimeException(e);
                            } catch (Exception e) {
                                log.error("创建表sql异常:", e);
                            }
                        }
                    });
                });
            }
        }
    }
}
